云端训练与真机评测
训练需要 NVIDIA GPU,Mac 做不了。本方案用 AutoDL,整条链路是:传代码和数据 → 装环境 → 训练 → 自动关机 → 取回模型 → 真机评测。下面全部是原生 scp / ssh / pip / lerobot-train 命令,不依赖任何包装脚本;把文中的 connect.westd.seetacloud.com 与 42143 换成自己实例控制台「登录指令」里的地址与端口即可。
| 步骤 | 在哪跑 | 核心命令 |
|---|---|---|
| 1 上传并解压 | Mac | scp -P … 传包 → ssh … "tar xzf …" 解压 |
| 2 装环境 | 云端 | pip install -e .、conda install "ffmpeg<7"、HF_ENDPOINT |
| 3 登录 wandb | 云端 | export WANDB_API_KEY=… 写进 ~/.bashrc |
| 4 启动训练 | 云端 | setsid nohup lerobot-train … |
| 5 自动关机 | 云端 | while pgrep …; do sleep 60; done; /usr/bin/shutdown |
| 6 取回模型 | Mac | scp -r …/pretrained_model models/<步数>/ |
| 7 查状态 | Mac | 一条 ssh 看完 checkpoint / 进程 / 日志 / 关机守护 |
8.1 步骤 1 · 上传并解压(Mac 上执行)
cd /Users/xxx/project/lerebot/autodl
# 一次传完代码包 + 数据集;远端目录必须写绝对路径(写 ~ 会被本地 shell 展开成本机家目录)
scp -P 42143 lerobot-main.tar.gz so101_demo.tar.gz \
root@connect.westd.seetacloud.com:/root/autodl-tmp/
# 解压(在云端执行,不必先登进去)
ssh -p 42143 root@connect.westd.seetacloud.com \
"cd /root/autodl-tmp && tar xzf lerobot-main.tar.gz && tar xzf so101_demo.tar.gz && ls -d lerobot-main so101_demo"- 地址与端口从 AutoDL 控制台的「登录指令」里抄。无卡模式与正常模式开机后都可能变化,连不上先核对这里。
- 只更新代码时,把数据集从
scp的文件列表里去掉即可(代码包通常是几百 KB,很快)。 scp按整文件传输,中断了重跑会从头再传。想续传就换成rsync -P --partial -e "ssh -p 42143" 本地文件 root@…:/root/autodl-tmp/。- 传完可核对完整性:本地
shasum -a 256 so101_demo.tar.gz对比云端sha256sum /root/autodl-tmp/so101_demo.tar.gz,一致才继续。
8.2 步骤 2 · 装环境(ssh 登进云端后按顺序执行)
ssh -p 42143 root@connect.westd.seetacloud.com
# ① GPU 是否可见
nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader
# ② 镜像源:AutoDL 预置的清华 pkgs/free 已下线(404),必须先移除
conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free 2>/dev/null
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
# ③ 确认镜像自带的 torch 是 2.8 + CUDA 12.8(不对就换官方 PyTorch 2.8 镜像重建实例)
python -c "import torch; print('torch', torch.__version__, 'cuda', torch.version.cuda)"
# ④ 装 lerobot(这个 fork 是 0.4.x,没有 [training] extra,依赖都在 base 里)
cd /root/autodl-tmp/lerobot-main && pip install -e .
# ⑤ GPU 视频解码:缺了会退成 CPU 软解,训练速度约减半
python -c "import torchcodec" || conda install -y -c conda-forge "ffmpeg<7"
# ⑥ HuggingFace 镜像(首次下载数据集或模型时才用得上)
grep -q hf-mirror ~/.bashrc || echo 'export HF_ENDPOINT=https://hf-mirror.com' >> ~/.bashrctorchcodec:缺
torchcodec会退化成 CPU 软解视频,训练速度大约减半。上面第 ⑤ 步就是用conda install -c conda-forge "ffmpeg<7"补齐它的依赖;若补完仍加载失败,通常是缺libnvrtc——能训,但慢。
最后自检一遍数据集,确认云端能读、条数对得上:
cd /root/autodl-tmp && python -c "
from lerobot.datasets.lerobot_dataset import LeRobotDataset
ds = LeRobotDataset('local/so101_demo', root='/root/autodl-tmp/so101_demo')
print(ds.meta.total_episodes, ds.meta.total_frames, ds.fps, ds.meta.camera_keys)
"8.3 步骤 3 · 登录 wandb(训练前必做,缺了训练会直接失败)
训练命令带 --wandb.enable=true,没有凭据会报 api_key not configured,训练起不来。登录方式就是把 key 导出成环境变量——wandb.init() 会自动读它,不需要额外的登录工具,在云端敲三条命令即可:
# 1. 导出到当前 shell —— 训练就靠这个变量
export WANDB_API_KEY='wandb_v1_你的key'
# 2. 写进 ~/.bashrc —— 重开机、新开终端后依然有效
echo "export WANDB_API_KEY='$WANDB_API_KEY'" >> ~/.bashrc
# 3. 验证:必须打印出 wandb_v1_ 开头的串,否则训练会失败
echo $WANDB_API_KEYkey 从 wandb.ai/authorize 拿(登录后直接显示)。key 是敏感凭据,别外发;要轮换就去 wandb.ai/settings 换新的,然后重新执行上面三条命令。
曲线在 wandb 网页上看(日志里会打印 Track this run --> 链接);不想用 wandb 的话,把训练命令里的 --wandb.enable=true 改成 false 也能跑,只是只剩终端日志了。
8.4 步骤 4 · 启动训练
setsid nohup /root/miniconda3/bin/lerobot-train \
--dataset.repo_id=local/so101_demo \
--dataset.root=/root/autodl-tmp/so101_demo \
--policy.type=act \
--output_dir=/root/autodl-tmp/outputs/train/act_so101_demo \
--job_name=act_so101_demo \
--batch_size=32 \
--save_freq=5000 \
--num_workers=12 \
--policy.device=cuda \
--wandb.enable=true \
--policy.push_to_hub=false \
--steps=25000 \
> /root/autodl-tmp/so101_train.log 2>&1 < /dev/null &
# 起来了吗:5 秒后看进程,然后跟日志
sleep 5; pgrep -f '[l]erobot-train'
tail -f /root/autodl-tmp/so101_train.log关键就在开头结尾这几样:setsid 让训练脱离当前会话、nohup 忽略挂断信号、末尾的 & 丢后台,再加上 > 日志 2>&1 < /dev/null 把输出写文件、标准输入接空——少了最后那个 < /dev/null,进程仍会被终端绑住。做完这步断开 SSH、关掉 Mac 都不影响。一次真实训练的参数与实测结果:
| 项目 | 值 | 说明 |
|---|---|---|
| 数据集 | local/so101_demo | 自录 31 集,两路相机 |
--steps | 25000 | 步数越多越久,但不一定越好 |
--batch_size | 32 | 显存不够就调小 |
--save_freq | 5000 | 每 5000 步存一个 → 全程 5 个 checkpoint |
--policy.device | cuda | 在 Mac 上会直接失败 |
| 实测速度 | 约 3.8 it/s | 日志里 updt_s:0.264 → 1 / 0.264 |
| 总耗时 | 约 1 小时 50 分 | 跑满 25000 步 |
| 最终 loss | 0.031 | ACT 的动作 L1 损失,收敛正常 |
| 收尾标志 | End of training | 看到这行才算正常跑完 |
怎么判断跑完了:两个条件同时满足:① 远端
checkpoints/下有 5 个纯数字目录(005000…025000,另有软链接last);② 日志最后一行是End of training。训练中会周期性打印step:25K loss:…进度行;tqdm 进度条给出[已用<剩余, it/s],「剩余」直接用来估算完成时刻。
8.5 步骤 5 · 训练完自动关机(省 GPU 空转的钱)
训练常在凌晨跑完,人不在电脑前。不用传守护脚本,直接在云端后台挂一条 bash -c 就够:先等训练进程出现(等不到就自己退出,绝不误关机)→ 每 60 秒轮询一次 → 结束后延迟 60 秒、sync 落盘再关机。
cd /root/autodl-tmp
setsid nohup bash -c '
for i in $(seq 30); do pgrep -f "[l]erobot-train" >/dev/null && break; sleep 10; done
pgrep -f "[l]erobot-train" >/dev/null || { echo "没等到训练进程,不关机"; exit 0; }
while pgrep -f "[l]erobot-train" >/dev/null; do sleep 60; done
echo "训练结束,60 秒后关机"; sleep 60; sync; /usr/bin/shutdown
' > auto_shutdown.log 2>&1 < /dev/null &
# 确认守护真的在跑,以及它打出的第一行日志
sleep 5; cat /root/autodl-tmp/auto_shutdown.log
ps -eo pid,cmd | grep '[b]ash -c' | head -38.6 步骤 6 · 取回模型(Mac 上执行)
云端 checkpoints/<步数>/ 下有两部分,评测只需要前者:
| 目录 | 大小 | 用途 |
|---|---|---|
pretrained_model/ | 约 206MB | 要下载:权重 + 配置 + 归一化参数,推理全靠它 |
training_state/ | 约 412MB | 不用下载:优化器动量、调度器与 RNG 状态,只在断点续训时有用 |
HOST=root@connect.westd.seetacloud.com
PORT=42143
OUT=/root/autodl-tmp/outputs/train/act_so101_demo
# ① 先看远端有哪些 checkpoint(完整训练应有 5 个纯数字目录:005000 … 025000)
ssh -p $PORT $HOST "ls -1 $OUT/checkpoints/"
# ② 在云端把各 pretrained_model 打成一个包(只打前者,training_state 不要)
ssh -p $PORT $HOST "cd $OUT/checkpoints && tar cf /root/autodl-tmp/pm.tar */pretrained_model"
# ③ 拉回本地并就地解开 → models/005000/pretrained_model … models/025000/pretrained_model
cd /Users/xxx/project/lerebot
scp -P $PORT $HOST:/root/autodl-tmp/pm.tar /tmp/pm.tar && tar xf /tmp/pm.tar -C models/
du -sh models/*
# ④ 校验:两端 model.safetensors 的字节数必须完全一致
ssh -p $PORT $HOST "stat -c '%s %n' $OUT/checkpoints/*/pretrained_model/model.safetensors"
stat -f '%z %N' models/*/pretrained_model/model.safetensors
# ⑤ 收尾:删掉云端那个大包
ssh -p $PORT $HOST "rm -f /root/autodl-tmp/pm.tar"- 合并成一次传输:逐个
scp会握多次手、密码也要输多遍。先tar再拉只要一条连接。想再省事就把ssh/scp都加上-o ControlMaster=auto -o ControlPath=/tmp/lr_cm/%r@%h:%p -o ControlPersist=600复用连接,全程只输一次密码;或者干脆配公钥:ssh-copy-id -p 42143 root@…。 - 不要用
expect之类自动喂密码:容易死锁,人在终端前手输最稳。 - 中途断了就重跑:
scp是整文件传输,会从头覆盖;云端那个pm.tar还在,直接重跑 ③ 即可,不必再打包。 - 别用「文件存在」判断完成:下到一半断掉会留下半截文件,看着像成功。所以第 ④ 步一定要比字节数,不一致就把
models/<步数>删掉重下。
8.7 真机评测
和「录制数据集」其实是同一条 lerobot-record,唯一的关键区别:把 --teleop.* 换成 --policy.path——动作不再由主臂产生,而是模型实时预测。它仍然会录数据集,用来记录这次评测的过程(方便回看哪几条成功)。源码里 RecordConfig 的 teleop 与 policy 是二选一,两者都不给才报错。
lerobot-record \
--robot.disable_torque_on_disconnect=true \
--robot.type=so101_follower \
--robot.port=/dev/tty.usbmodem5B8E1136841 \
--robot.id=my_awesome_follower_arm \
--robot.cameras="{wrist: {type: opencv, index_or_path: 2, width: 640, height: 480, fps: 30}, \
environment: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
--policy.path=/Users/xxx/project/lerebot/models/025000/pretrained_model \
--policy.device=mps \
--dataset.repo_id=local/eval_025000 \
--dataset.root=/Users/xxx/project/lerebot/datasets/eval_025000 \
--dataset.single_task="Grab the black cube" \
--dataset.num_episodes=10 \
--dataset.episode_time_s=30 \
--dataset.reset_time_s=10 \
--dataset.push_to_hub=false \
--display_data=true启动前必查:
项 要求 不符的后果 数据集名 斜杠后必须以 eval_开头(local/eval_025000);反向也强制——不带--policy.path的录制反而不能用eval_开头。换步数就换个名字ValueError: … does not begin with 'eval_';反向报no policy is provided;共用名字会撞FileExistsError或被当成续录--policy.path指到 …/checkpoints/<步数>/pretrained_model这一层错一层就加载不了;加载成功时日志回显 'pretrained_path',可用它自检相机配置 键名、分辨率、fps 与训练时完全一致 归一化参数对不上 → 报错或效果崩坏( input_features应为(3, 480, 640))--policy.devicemps写 cuda直接失败(Mac 没有);启动时那句Switching to 'mps'属正常,不是错误以上校验都在建数据集之前完成,报错不留垃圾目录,改名重跑即可。首次执行先小批量试:
--dataset.num_episodes=1 --dataset.episode_time_s=10。